<?php

namespace App\Supports\Withdraw;

use App\Models\User;
use App\Models\Withdraw;
use Brick\Math\BigDecimal;
use Brick\Math\RoundingMode;

class WithdrawBase
{
	/**
	 * @var User
	 */
	protected $targetUser = null;
	
	protected $targetChannel = null;
	
	protected $targetMethod = null;
	
	protected $targetAccount = null;
	
	/**
	 * How much user wants to withdraw.
	 * @var BigDecimal
	 */
	protected $targetAmount = 0.00;
	
	protected $chargeType;
	protected $chargeFee;
	
	protected $actualAmount;
	
	/**
	 * @var Withdraw
	 */
	protected $withdraw;
	
	public function __construct($withdraw = null)
	{
		if ($withdraw) {
			$this->setRequiredParamsFromModel($withdraw);
		} else {
			$this->setRequiredParamsFromHttp();
		}
	}
	
	public function setRequiredParamsFromHttp()
	{
		if (request('withdraw_amount') or request('withdraw_method')) {
			request()->validate([
				'withdraw_amount' => 'required|numeric',
			]);
			
			$this->targetUser = request()->user();
			$this->targetChannel = $this->getChannel();
			$this->targetMethod = 'alipay';
			$this->targetAmount = big_decimal(request('withdraw_amount'));
			
			$this->chargeType = $this->getChargeType();
			$this->chargeFee = $this->getChargeFee();
			$this->actualAmount = $this->getActualAmount();
		}
	}
	
	public function setRequiredParamsFromModel(Withdraw $withdraw)
	{
		$this->withdraw = $withdraw;
		$this->targetChannel = $withdraw->channel;
		$this->targetUser = $withdraw->user;
		$this->targetMethod = $withdraw->method;
		$this->targetAmount = big_decimal($withdraw->target_amount);
	}
	
	public function isWithdrawOpened()
	{
		return (bool)cs('withdraw')->is_open;
	}
	
	public function isPhoneBound()
	{
		return $this->targetUser->isPhoneBound();
	}
	
	public function isAccountBound()
	{
		if ($this->targetMethod === 'alipay') {
			return $this->targetUser->isAlipayBound();
		}
		if ($this->targetMethod === 'wxpay') {
			return $this->targetUser->isWeixinBound();
		}
		return false;
	}
	
	public function isAppliedToday()
	{
		return $this->targetUser->hasAppliedWithdrawToday();
	}
	
	public function isMethodSupport()
	{
		return in_array($this->targetMethod, array_keys(config("site.withdraw.methods")));
	}
	
	public function withdrawMultiple()
	{
		return cs('withdraw')->multiple;
	}
	
	public function isMultipleMatched()
	{
		if ($this->withdrawMultiple() > 0) {
			return $this->targetAmount->remainder($this->withdrawMultiple())->isEqualTo(0);
		}
		return true;
	}
	
	public function getAllowedLowestAmount()
	{
		return big_decimal(cs('withdraw')->allowed_lowest_amount);
	}
	
	public function getAllowedHighestAmount()
	{
		return big_decimal(cs('withdraw')->allowed_highest_amount);
	}
	
	public function isLowestAmountMatched()
	{
		if ($this->getAllowedLowestAmount()->isGreaterThan(0)) {
			return $this->targetAmount->isGreaterThanOrEqualTo($this->getAllowedLowestAmount());
		}
		return true;
	}
	
	public function isHighestAmountMatched()
	{
		if ($this->getAllowedHighestAmount()->isGreaterThan(0)) {
			return $this->targetAmount->isLessThanOrEqualTo($this->getAllowedHighestAmount());
		}
		return true;
	}
	
	public function getChannel()
	{
		return cs('withdraw')->allowed_channel;
	}
	
	public function getChargeType()
	{
		return cs('withdraw')->charge_type;
	}
	
	public function getChargePercent()
	{
		$chargePercent = big_decimal((string)cs('withdraw')->charge_percent);
		if ($chargePercent->isGreaterThanOrEqualTo(0)) {
			return $chargePercent;
		}
		return big_decimal(0);
	}
	
	public function getLimitedLowestChargeFee()
	{
		return big_decimal(cs('withdraw')->allowed_lowest_charge);
	}
	
	public function getLimitedHighestChargeFee()
	{
		return big_decimal(cs('withdraw')->allowed_highest_charge);
	}
	
	public function getChargeFee()
	{
		if ($this->getChargePercent()->isLessThanOrEqualTo(0)) {
			return big_decimal(0);
		}
		
		$fee = $this->targetAmount->multipliedBy((string)$this->getChargePercent()/100);
		
		/**
		 * Greater than 0 means the backend has set the rule.
		 */
		if (($this->getLimitedLowestChargeFee()->isGreaterThan(0)) and
			$fee->isLessThan($this->getLimitedLowestChargeFee())
		) {
			return $this->getLimitedLowestChargeFee();
		}
		
		/**
		 * Greater than 0 means the backend has set the rule.
		 */
		if (($this->getLimitedHighestChargeFee()->isGreaterThan(0)) and
			$fee->isGreaterThan($this->getLimitedHighestChargeFee())
		) {
			return $this->getLimitedHighestChargeFee();
		}
		
		return $fee;
	}
	
	public function isUserBalanceEnough()
	{
		if ($this->getChargeType() === 'in') {
			$balance = big_decimal($this->targetUser->balance);
			return $balance->isGreaterThanOrEqualTo($this->targetAmount);
		}
		if ($this->getChargeType() === 'out') {
			$balance = big_decimal($this->targetUser->balance);
			$allTargetMoney = $this->targetAmount->plus($this->getChargeFee())->toScale(2, RoundingMode::HALF_UP);
			return $balance->isGreaterThanOrEqualTo($allTargetMoney);
		}
		return false;
	}
	
	public function getActualAmount()
	{
		if ($this->getChargeType() === 'in') {
			return $this->targetAmount->minus($this->getChargeFee())->toScale(2, RoundingMode::HALF_UP);
		}
		if ($this->getChargeType() === 'out') {
			return $this->targetAmount;
		}
		return $this->targetAmount;
	}
}
